﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ColorClusteringSOM
{
    /// <summary>
    /// Trida ktera pomaha s vypocty
    /// TODO: Mozna oznacit sealed nebo tak neco
    /// </summary>
    public class Helper
    {



        



        /// <summary>
        /// Vrati Euklidovskou vzdalenost
        /// </summary>
        /// <param name="input">vstupy</param>
        /// <param name="weights">vahy</param>
        /// <returns></returns>
        public double EuklidDistance(double[] input, double[] weights)
        {

            double euDistance = 0;
            double pom = 0;

            for (int i = 0; i < input.Length; i++)
            {
                pom = input[i] - weights[i];
                euDistance += pom * pom;
            }

            return Math.Sqrt(euDistance);

        }

        /// <summary>
        /// POUZIVAT!!!
        /// </summary>
        /// <param name="neuronWeights"></param>
        /// <param name="leaderWeights"></param>
        /// <returns></returns>
        public double EuklidDistanceBetweenNeurons(Neuron neuron, NeuronRough leader)
        {
            double euDistance = 0;
            double pom = 0;


            for (int i = 0; i < leader.LeaderWeight.Length; i++)
            {
                pom = neuron.Weights[i] - leader.LeaderWeight[i];
                euDistance += pom * pom;
            }

            return Math.Sqrt(euDistance);
        }

        /// <summary>
        /// POUZIVAT!!!2
        /// </summary>
        /// <param name="neuronWeights"></param>
        /// <param name="leaderWeights"></param>
        /// <returns></returns>
        public double EuklidDistanceBetweenNeurons2(NeuronRough neuron, NeuronRough leader)
        {
            double euDistance = 0;
            double pom = 0;


            for (int i = 0; i < leader.LeaderWeight.Length; i++)
            {
                pom = neuron.Neuron.Weights[i] - leader.LeaderWeight[i];
                euDistance += pom * pom;
            }

            return Math.Sqrt(euDistance);
        }


        /// <summary>
        /// POUZIVAT!!!STATIC
        /// </summary>
        /// <param name="neuronWeights"></param>
        /// <param name="leaderWeights"></param>
        /// <returns></returns>
        public static double EuklidDistanceBetweenNormalNeurons(Neuron neuron, Neuron leader)
        {
            double euDistance = 0;
            double pom = 0;


            for (int i = 0; i < leader.Weights.Length; i++)
            {
                pom = neuron.Weights[i] - leader.Weights[i];
                euDistance += pom * pom;
            }

            return Math.Sqrt(euDistance);
        }


        public String PrintWeights(double[] weigts, String s)
        {
            String str = s;
            for (int i = 0; i < weigts.Length; i++)
            {
                str += " " + weigts[i] ;
            }
            return str;
        }


        public static double[] ZeroMatrix(int dimension)
        {
            double[] d = new double[dimension];

            for (int i = 0; i < d.Length; i++)
            {
                d[i] = 0;
            }

            return d;
        }

        /// <summary>
        /// Metoda, ktera vrati vahovy vektor, ktery uz je vynasobeny
        /// w_lower nebo w_upper
        /// </summary>
        /// <param name="ar1">vahy leadra</param>
        /// <param name="ar2">vahy neur</param>
        /// <returns></returns>
        public static double[] RoughtSum(List<NeuronRough> approximation, double importance, int dimension)
        {
            double[] result = ZeroMatrix(dimension);
            double count = approximation.Count;

            foreach (NeuronRough n in approximation)
            {
                
                //secteni vah
                for (int i = 0; i < n.Neuron.Weights.Length; i++)
                {
                    result[i] += n.Neuron.Weights[i];
                }
            }

            for (int i = 0; i <  result.Length; i++)
            {
                result[i] = (result[i] / count) * importance;
            }

            return result;
        }

        /// <summary>
        /// Jednoduche secteni poli
        /// </summary>
        /// <param name="ar1"></param>
        /// <param name="ar2"></param>
        /// <returns></returns>
        public static double[] SumArrays(double[] ar1, double[] ar2)
        {
            double[] result = new double[ar1.Length];

            for (int i = 0; i < ar1.Length; i++)
            {
                result[i] = ar1[i] + ar2[i];
            }
            return result;
        }

        /// <summary>
        /// Vrati Euklidovskou vzdalenost BLBOST
        /// </summary>
        /// <param name="neuron">NeuronRough</param>
        /// <param name="leader">Neuron</param>
        /// <returns></returns>
        public double EuklidDistancePREDELAT(Neuron neuron, NeuronRough leader)
        {
            double xs = (neuron.X - leader.X) * (neuron.X - leader.X);
            double ys = (neuron.Y - leader.Y) * (neuron.Y - leader.Y);

            double distance = Math.Sqrt(xs + ys);
            return distance;
        }

        /// <summary>
        /// Vrati viteze 
        /// </summary>
        /// <param name="input"></param>
        /// <param name="rows"></param>
        /// <param name="cols"></param>
        /// <param name="network"></param>
        /// <returns></returns>
        public Neuron Winner(double[] input, int rows, int cols, Network network)
        {

            Neuron winner = null;
            double euklid = double.MaxValue;

            //jen pro tisk hodnot
            int ii = 0;
            int jj = 0;

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    Neuron actualNeuron = network[i, j];

                    //euklidovska vzdalenost : vstup a vahy neuronu
                    double actualEuklid = EuklidDistance(input, actualNeuron.Weights);
                    if (actualEuklid < euklid) // (actualEuklid <= euklid) ??? 
                    {
                        winner = actualNeuron;
                        euklid = actualEuklid;

                        ii = i;
                        jj = j;

                    }
                }
            }

            return winner;

        }

        /// <summary>
        /// Vrati viteze 
        /// </summary>
        /// <param name="input"></param>
        /// <param name="rows"></param>
        /// <param name="cols"></param>
        /// <param name="network"></param>
        /// <returns></returns>
        public Neuron Winner(int[] input, int rows, int cols, Network network)
        {

            double[] Dinput = NormalizeInput(input);
            Neuron winner = null;
            double euklid = double.MaxValue;

            //jen pro tisk hodnot
            int ii = 0;
            int jj = 0;

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    Neuron actualNeuron = network[i, j];

                    //euklidovska vzdalenost : vstup a vahy neuronu
                    double actualEuklid = EuklidDistance(Dinput, actualNeuron.Weights);
                    if (actualEuklid < euklid) // (actualEuklid <= euklid) ??? 
                    {
                        winner = actualNeuron;
                        euklid = actualEuklid;

                        ii = i;
                        jj = j;

                    }
                }
            }

            return winner;

        }


        /// <summary>
        /// Metoda co normalizuje cele pole a prevede ho double[]
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public double[] NormalizeInput(int[] data)
        {
            double[] Dinput = new double[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                double input = data[i];
                Dinput[i] = (input / 256);
            }

            return Dinput;
        }


        /// <summary>
        /// RGB
        /// Normalizuje vstup na hodnotz na rozsah[0,1]
        ///* N = (input - Vmin)/(Vmax - Vmin)
        ///* N = (input - 0)/(255 - 0)
        ///* Vmin = 0
        ///* Vmax = 255
        ///*/
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public double Normalization(int input)
        {
            double Dinput = input;
            // Console.WriteLine("input=" + input + "   Dinput/255=" + Dinput / 255);
            return (Dinput / 256);
        }

    }
}
